package com.personal.dataconvert.util;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.personal.core.data.DataColumn;
import com.personal.core.data.DataRow;
import com.personal.core.data.DataTable;
import com.personal.core.utils.Assert;
import com.personal.core.utils.CoreUtil;
import com.personal.dataconvert.IrregularDataTable2Excel;
import com.personal.dataconvert.IrregularDataTable2Html;
import com.personal.dataconvert.bean.CombineColumnConfig;
import com.personal.dataconvert.bean.HeaderConfig;

/**
 * 合并DataTable工具类
 * 将规则表按照配置转成不规则表
 * @author cuibo
 *
 */
public class CombineDataTableUtil
{
    private static final String COL = "_COL";

    private static final String COLSPAN = "_COLSPAN";

    private static final String DEFAULT = "_DEFAULT";

    private static final String ROW = "_ROW";

    private static final String ROWSPAN = "_ROWSPAN";

    /**
     * 合并DataTable
     * @param table   合并的DataTable   列宽信息，样式信息，放在 column 中
     * tag ：可以设置居左居右的样式
     * dataType : 中可以设置宽度
     * @param columnConfigs  合并配置
     * @param hideCol  可以为空
     * @return  返回的DataTable 是博微不规则报表的格式： 使用字段DATA,COLINDEX,ROWINDEX,MERGEROWCOUNT,MERGECOLCOUNT
     */
    public static DataTable combineDataTable(DataTable table, Set<? extends CombineColumnConfig> columnConfigs, Set<Integer> hideCol)
            throws Exception
    {
        if (table == null)
        {
            return null;
        }
        Assert.isNotNullOrEmpty(table.getColumns(), "数据集列配置信息为空！");
        if (table.getRows() == null || table.getRows().isEmpty())
        {
            return null;
        }
        // 第一步为 table默认赋值一个特殊值和跨行，跨列均为1，并计算其所在行所在列(此时是规则表状态的所在行，所在列)
        CombineDataTableUtil.initDefaultInfo(table);
        // 第二步 根据配置计算,跨行，跨列，
        CombineDataTableUtil.calculateDataTable(table, columnConfigs);
        // 第三部 返回不规则报表
        return CombineDataTableUtil.buildIregularDataTable(table, hideCol);
    }
    
    /**
     * 合并数据
     * @param leafConfigs
     * @param sheetData
     * @param columnConfigs
     * @return
     * @throws Exception
     */
    public static DataTable combineDataTable(List<HeaderConfig> leafConfigs, List<Map<String, Object>> sheetData, Set<? extends CombineColumnConfig> columnConfigs)
            throws Exception
    {
        Assert.isNotNullOrEmpty(leafConfigs, "数据集列配置信息为空！");
        if (sheetData == null || sheetData.isEmpty())
        {
            return null;
        }
        DataTable table = new DataTable();
        DataColumn column = null;
        // 处理隐藏的问题，如果allLeftConfigs中的列设置了隐藏，则将对应的列隐藏
        Set<Integer> hideCol = new HashSet<Integer>();
        int colIndex = 0;
        for (HeaderConfig headerConfig : leafConfigs)
        {
            column = new DataColumn(headerConfig.getValue(), headerConfig.getDisplayName());
            // 设置样式
            column.setWidth(headerConfig.getWidth());
            column.setAlign(headerConfig.getAlign());
            table.getColumns().add(column);
            if (!headerConfig.isDisplay())
            {
                hideCol.add(colIndex);
            }
            colIndex ++;
        }
       
        DataRow row = null;
        for (Map<String, Object> map : sheetData)
        {
            row = table.newRow();
            row.getItemMap().putAll(map);
            table.getRows().add(row);
        }
        return combineDataTable(table, columnConfigs, hideCol);
    }

    /**
     * 获取行中 obj 的字符串形式
     * @param obj
     * @return
     */
    public static String getStringValue(Object obj)
    {
        if (obj == null)
        {
            return "";
        } else if (obj instanceof Date)
        {
            return CoreUtil.formatDate(obj);
        } else
        {
            return CoreUtil.parseStr(obj);
        }
    }

    /**
     * 构建返回的不规则报表
     * @param table
     * @param hideCol  隐藏的列
     * @return
     */
    private static DataTable buildIregularDataTable(DataTable table, Set<Integer> hideCol)
    {
        DataTable result = new DataTable();
        // 添加不规则的列 DATA,COLINDEX,ROWINDEX,MERGEROWCOUNT,MERGECOLCOUNT
        result.addNewColumn(ExcelHtmlUtil.DATA);
        result.addNewColumn(ExcelHtmlUtil.COLINDEX);
        result.addNewColumn(ExcelHtmlUtil.ROWINDEX);
        result.addNewColumn(ExcelHtmlUtil.MERGEROWCOUNT);
        result.addNewColumn(ExcelHtmlUtil.MERGECOLCOUNT);
        result.addNewColumn(ExcelHtmlUtil.WIDTH);
        result.addNewColumn(ExcelHtmlUtil.STYLE);
        result.addNewColumn(ExcelHtmlUtil.EXCELSTYLE);
        result.addNewColumn(ExcelHtmlUtil.CLICK);
        result.addNewColumn(ExcelHtmlUtil.ALIGN);
        // cb 20170322 新增备注功能
        result.addNewColumn(ExcelHtmlUtil.POSTIL);
        result.addNewColumn(ExcelHtmlUtil.TDDATAMAP);
        // 新增td样式，rederror blueerror 功能
        result.addNewColumn(ExcelHtmlUtil.CLASS);
        result.addNewColumn(ExcelHtmlUtil.REDERROR);
        result.addNewColumn(ExcelHtmlUtil.BLUEERROR);
        // 新增trclass
        result.addNewColumn(ExcelHtmlUtil.TRCLASS);

        DataRow newRow = null;
        
        StringBuilder tdStyle = null;
        for (DataRow row : table.getRows())
        {
            for (DataColumn column : table.getColumns())
            {
                // 如果默认值为true才添加进去
                if (CoreUtil.parseBoolean(row.getItemMap().get(
                        column.getColumnName() + CombineDataTableUtil.DEFAULT)))
                {
                    tdStyle = new StringBuilder();
                    newRow = result.newRow();
                    newRow.setValue(ExcelHtmlUtil.DATA, row.getItemMap().get(column.getColumnName()));
                    newRow.setValue(ExcelHtmlUtil.COLINDEX, row.getItemMap()
                            .get(column.getColumnName() + CombineDataTableUtil.COL));
                    newRow.setValue(ExcelHtmlUtil.ROWINDEX, row.getItemMap()
                            .get(column.getColumnName() + CombineDataTableUtil.ROW));
                    newRow.setValue(ExcelHtmlUtil.MERGECOLCOUNT,
                            row.getItemMap().get(column.getColumnName() + CombineDataTableUtil.COLSPAN));
                    newRow.setValue(ExcelHtmlUtil.MERGEROWCOUNT,
                            row.getItemMap().get(column.getColumnName() + CombineDataTableUtil.ROWSPAN));

                    // 设置样式和宽度
                    newRow.setValue(ExcelHtmlUtil.WIDTH, column.getWidth());
                    // 水平排列方式
                    newRow.setValue(ExcelHtmlUtil.ALIGN, column.getAlign());
                    if (HeaderConfig.LEFT.equals(column.getAlign()))
                    {
                        tdStyle.append(" style=\"text-align:left;align:left;");
                    } else if (HeaderConfig.RIGHT.equals(column.getAlign()))
                    {
                        tdStyle.append(" style=\"text-align:right;align:right;");
                    } else
                    {
                        tdStyle.append(" style=\"text-align:center;align:center;");
                    }
                    // 看有没有手动设置样式
                    if (!CoreUtil.isEmpty(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDSTYLE)))
                    {
                        tdStyle.append(CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDSTYLE)));
                        // 生成Excel用
                        newRow.setValue(ExcelHtmlUtil.EXCELSTYLE, CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDSTYLE)));
                    }
                    // 判断是否隐藏
                    if (hideCol.contains(newRow.getItemMap().get(ExcelHtmlUtil.COLINDEX)))
                    {
                        tdStyle.append(" display:none; ");
                    }
                    tdStyle.append("\" ");
                    // 生成HTML用
                    newRow.getItemMap().put(ExcelHtmlUtil.STYLE, tdStyle.toString());
                    // 2017 03 13 新增事件的支持
                    newRow.getItemMap().put(ExcelHtmlUtil.CLICK, CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDCLICK)));
                    // 20174 03 22 新增备注的支持
                    newRow.getItemMap().put(ExcelHtmlUtil.POSTIL, CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Excel.TDPOSTIL)));
                    newRow.getItemMap().put(ExcelHtmlUtil.TDDATAMAP, row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDDATAMAP));
                    // 新增class的设置和redError和blueError的功能
                    if (!CoreUtil.isEmpty(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDCLASS)))
                    {
                        newRow.getItemMap().put(ExcelHtmlUtil.CLASS, " class = \"" + CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDCLASS)) + "\" ");
                    }
                    if (!CoreUtil.isEmpty(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDRED)))
                    {
                        newRow.getItemMap().put(ExcelHtmlUtil.REDERROR, " rederror = \"" + CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDRED)) + "\" ");
                    }
                    if (!CoreUtil.isEmpty(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDBLUE)))
                    {
                        newRow.getItemMap().put(ExcelHtmlUtil.BLUEERROR, " blueerror = \"" + CoreUtil.parseStr(row.getItemMap().get(column.getColumnName() + IrregularDataTable2Html.TDBLUE)) + "\" ");
                    }
                    if (!CoreUtil.isEmpty(row.getItemMap().get(IrregularDataTable2Html.TRCLASS)))
                    {
                        newRow.getItemMap().put(ExcelHtmlUtil.TRCLASS, CoreUtil.parseStr(row.getItemMap().get(IrregularDataTable2Html.TRCLASS)));
                    }
                    result.getRows().add(newRow);
                }
            }
        }

        return result;
    }

    private static void calculateDataTable(DataTable table, Set<? extends CombineColumnConfig> columnConfigs)
    {
        // 不需要合并
        if (columnConfigs == null || columnConfigs.isEmpty())
        {
            return;
        }
        for (CombineColumnConfig columnConfig : columnConfigs)
        {
            if (CombineColumnConfig.横向合并.equals(columnConfig.getHandleType()))
            {
                CombineDataTableUtil.combineX(table, columnConfig);
            } else if (CombineColumnConfig.纵向合并.equals(columnConfig.getHandleType()))
            {
                CombineDataTableUtil.combineY(table, columnConfig, columnConfigs);
            }
        }
    }

    /**
     * columnConfigs 是否包含 column 这一列
     * @param columnConfigs
     * @param lastColumn
     * @return  包含  包含但不校验  不包含
     */
    private static String columnConfigContainsColumn(Set<? extends CombineColumnConfig> columnConfigs,
            DataColumn column)
    {
        if (columnConfigs == null || columnConfigs.isEmpty())
        {
            return "不包含";
        }
        for (CombineColumnConfig columnConfig : columnConfigs)
        {
            // cb 2016 12 26 去除  singleMaxCount 等于 1的情况，因为如果强制至为1，那么其是百分百不允许合并，不要加到判断的集中
            if (columnConfig.getColumnValue().equals(column.getColumnName()))
            {
                if (columnConfig.getSingleMaxCount() == 1)
                {
                    return "包含但不校验";
                } else
                {
                    return "包含";
                }
            }
        }
        return "不包含";
    }

    /**
     * 横向合并
     * @param table
     * @param columnConfig
     */
    private static void combineX(DataTable table, CombineColumnConfig columnConfig)
    {
        // 没有合并列则不需要处理
        if (CoreUtil.isEmpty(columnConfig.getColumnValue()))
        {
            return;
        }
        // 查找开始合并列的位置
        int index = 0;
        boolean matchCol = false;

        for (DataColumn column : table.getColumns())
        {
            if (columnConfig.getColumnValue().equals(column.getColumnName()))
            {
                matchCol = true;
                break;
            }
            index++;
        }
        // 没有找到合并的列
        if (!matchCol)
        {
            return;
        }
        // 开始合并的行值
        String tempValue = "";
        String currentValue = "";

        // cb 2016 12 22 新增横向合并时可以指定行范围，下标0开始
        int rowIndex = -1;
        
        // 单次合并次数
        int singleCount = 0;
        sign:
        for (DataRow row : table.getRows())
        {
            // 每次初始化均为0
            singleCount = 0;
            
            rowIndex++;
            // 不在范围之内不合并
            if (rowIndex < columnConfig.getMinRowScope())
            {
                continue;
            }
            if (rowIndex >= columnConfig.getMaxRowScope())
            {
                break;
            }

            tempValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(columnConfig.getColumnValue()));
            // 不合并, 2016 12 08 对于均为空的情况也可以合并
            // 从开始位置往后遍历，如果相同，其跨列合并加1，并下一个值标记置为false
            for (int i = index + 1; i < table.getColumns().size(); i++)
            {
                // 如果超过指定的合并范围则退出
                if (i > columnConfig.getCombineCount())
                {
                    break;
                }
                currentValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(table.getColumns().get(i).getColumnName()));
                // 均为空也可以合并
                if (CoreUtil.checkEqual(tempValue, currentValue))
                {
                    // 单次合并次数 ++
                    singleCount++;
                    // 如果单次合并次数过多，则不允许合并
                    if (singleCount > columnConfig.getSingleMaxCount())
                    {
                        continue sign;
                    }
                    row.getItemMap().put(
                            columnConfig.getColumnValue() + CombineDataTableUtil.COLSPAN,
                            1 + CoreUtil.parseInt(row.getItemMap().get(
                                    columnConfig.getColumnValue() + CombineDataTableUtil.COLSPAN)));
                    // 值置为false
                    row.getItemMap().put(table.getColumns().get(i).getColumnName() + CombineDataTableUtil.DEFAULT,
                            false);
                } else
                {
                    // 只要出现不相同的则跳出当前行的合并
                    break;
                }
            }
        }

    }

    /**
     * 纵向合并
     * @param table
     * @param columnConfig
     * @param columnConfigs
     */
    private static void combineY(DataTable table, CombineColumnConfig columnConfig,
            Set<? extends CombineColumnConfig> columnConfigs)
    {
        // 没有合并列则不需要处理
        if (CoreUtil.isEmpty(columnConfig.getColumnValue()))
        {
            return;
        }
        // 没有当前列则不需要处理
        boolean matchCol = false;
        // 匹配到的列序号
        int matchColIndex = 0;
        for (DataColumn column : table.getColumns())
        {
            if (columnConfig.getColumnValue().equals(column.getColumnName()))
            {
                matchCol = true;
                break;
            }
            matchColIndex++;
        }
        // 没有找到合并的列
        if (!matchCol)
        {
            return;
        }

        String lastColValue = "";
        String lastRowValue = "";

        String tempValue = "";
        String currentValue = "";
        int tempIndex = 0;

        // 当前遍历到的行号
        int currentIndex = -1;

        // 单次合并的列数，有时候限制单次不允许合并过多列
        int singleCount = 0;

        sign: for (DataRow row : table.getRows())
        {
            currentIndex++;
            // 不在范围之内不合并
            if (currentIndex < columnConfig.getMinRowScope())
            {
                continue;
            }
            if (currentIndex >= columnConfig.getMaxRowScope())
            {
                break;
            }
            
            if (currentIndex == 0)
            {
                tempValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(columnConfig.getColumnValue()));
                tempIndex = currentIndex;
                continue;
            }
            // 超过最大合并数量则退出
            if (currentIndex > columnConfig.getCombineCount())
            {
                break;
            }
            currentValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(columnConfig.getColumnValue()));
            // 如果下一行和temp的值相等，则把tempIndex 处的行的跨行加 1,当前行的默认值置为false
            // cb 2016 12 08 增加都为空的情况下则相等的判断
            if (CoreUtil.checkEqual(tempValue, currentValue))
            {
                // cb  2016/10/13 如果改行的前 n 列中连续的”合并列“ 的值都不相等才允许合并
                if (matchColIndex > 0 && currentIndex > 0)
                {
                    List<DataColumn> columns = CombineDataTableUtil.getLastNColumns(matchColIndex, columnConfig,
                            columnConfigs, table);
                    // 上一行数据
                    DataRow lastRow = table.getRows().get(currentIndex - 1);
                    if (columns != null && columns.size() > 0)
                    {
                        for (DataColumn dataColumn : columns)
                        {
                            lastColValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(dataColumn.getColumnName()));
                            lastRowValue = CombineDataTableUtil.getStringValue(lastRow.getItemMap().get(dataColumn.getColumnName()));
                            if (!CoreUtil.checkEqual(lastColValue, lastRowValue))
                            {
                                // 一定要重新赋值 tempIndex
                                tempIndex = currentIndex;
                                continue sign;
                            }
                        }
                    }
                }
                // 单次合并次数 ++
                singleCount++;

                // 如果单次合并次数过多，则不允许合并
                if (singleCount > columnConfig.getSingleMaxCount())
                {
                    // 清空单次合并次数
                    singleCount = 0;
                    // 重置 temp 值
                    tempValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(columnConfig.getColumnValue()));
                    tempIndex = currentIndex;
                    continue;
                }
                // 合并处理
                table.getRows()
                        .get(tempIndex)
                        .getItemMap()
                        .put(columnConfig.getColumnValue() + CombineDataTableUtil.ROWSPAN,
                                1 + CoreUtil.parseInt(table.getRows().get(tempIndex).getItemMap()
                                        .get(columnConfig.getColumnValue() + CombineDataTableUtil.ROWSPAN)));
                // 默认值置为 false
                row.getItemMap().put(columnConfig.getColumnValue() + CombineDataTableUtil.DEFAULT, false);
                continue;
            } else
            {
                // 清空单次合并次数
                singleCount = 0;
                // 重置 temp 值
                tempValue = CombineDataTableUtil.getStringValue(row.getItemMap().get(columnConfig.getColumnValue()));
                tempIndex = currentIndex;
            }
        }
    }

    /**
     * 获取 columnConfig 的前N合并列
     * @param currentIndex 当前列号
     * @param columnConfig
     * @param columnConfigs
     * @param table
     * @return
     */
    private static List<DataColumn> getLastNColumns(int currentIndex, CombineColumnConfig columnConfig,
            Set<? extends CombineColumnConfig> columnConfigs, DataTable table)
    {
        List<DataColumn> columns = new ArrayList<DataColumn>();
        DataColumn column = null;
        
        String containsResult = "";
        for (int i = currentIndex - 1; i > -1; i--)
        {
            column = table.getColumns().get(i);
            containsResult = CombineDataTableUtil.columnConfigContainsColumn(columnConfigs, column);
            // cb 2016 12 26 去除  singleMaxCount 等于 1的情况，因为如果强制至为1，那么其是百分百不允许合并，不要加到判断的集中
            if ("包含".equals(containsResult))
            {
                columns.add(column);
            } else if ("包含但不校验".equals(containsResult))
            {
                continue;
            } else
            {
                break;
            }
        }
        return columns;
    }

    /**
     * table默认赋值一个特殊值和跨行，跨列均为1，并计算其所在行所在列(此时是规则表状态的所在行，所在列)
     * @param table
     */
    private static void initDefaultInfo(DataTable table)
    {
        int colIndex = 0;
        for (DataColumn column : table.getColumns())
        {
            int rowIndex = 0;
            for (DataRow row : table.getRows())
            {
                // 所在列
                row.getItemMap().put(column.getColumnName() + CombineDataTableUtil.COL, colIndex);
                row.getItemMap().put(column.getColumnName() + CombineDataTableUtil.ROW, rowIndex);
                // 默认跨行，跨列均为1
                row.getItemMap().put(column.getColumnName() + CombineDataTableUtil.COLSPAN, 1);
                row.getItemMap().put(column.getColumnName() + CombineDataTableUtil.ROWSPAN, 1);
                row.getItemMap().put(column.getColumnName() + CombineDataTableUtil.DEFAULT, true);
                rowIndex++;
            }
            colIndex++;
        }
    }

    /**
     * 创建不规则表
     * @param tableName
     * @param sheetData
     * @return
     */
	public static DataTable createIrregularTable(String tableName, List<Map<String, Object>> sheetData) 
	{
		DataTable result = new DataTable(tableName);
		result.addNewColumn(ExcelHtmlUtil.DATA);
		result.addNewColumn(ExcelHtmlUtil.COLINDEX);
		result.addNewColumn(ExcelHtmlUtil.ROWINDEX);
		result.addNewColumn(ExcelHtmlUtil.MERGEROWCOUNT);
		result.addNewColumn(ExcelHtmlUtil.MERGECOLCOUNT);
		if (CoreUtil.isEmpty(sheetData)) 
		{
			return result;
		}
		DataRow newRow = null;
		for (Map<String, Object> map : sheetData) 
		{
			newRow = result.newRow();
			Map<String, Object> newMap = new HashMap<String, Object>();
			for (Entry<String, Object> entry : map.entrySet()) 
			{
				if (CoreUtil.isEmpty(entry.getKey())) 
				{
					continue;
				}
				newMap.put(entry.getKey().toUpperCase(), entry.getValue());
			}
			newRow.getItemMap().putAll(newMap);
			result.getRows().add(newRow);
		}
		return result;
	}

}
